/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gclient;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 *
 * @author sajin
 */
public class Downloader implements Runnable {

	public Downloader(Vector<String> userList, String sha1FIleName, String fileName, int grpID, String userName, GalaxiaClientUI galaxiaClientUI) {
		downloaderThread = new Thread(this, "Downloader_Thread");

		this.galaxiaClientUI = galaxiaClientUI;
		// Make deep copies of formal parameters.except
		// Tracker communicator.
		this.sha1FileName = new String(sha1FIleName);
		this.userName = new String(userName);
		this.userList = new Vector<String>(userList);
		this.fileName = fileName;
		this.grpID = grpID;

		fileChunkSizeInBytes = 2048; // 2 KB.
		quit = false;
	}

	public void start() {
		try {
			getIPAddressList();
		} catch (IOException ex) {
			System.out.println(" Error Occured : " + ex.getMessage()  );
		}
		downloaderThread.start();
	}

	public void stop() {
		quit = true;
	}

	@Override
	public void run() {
			int nChunks = findChunks();
			
			System.out.println("Starting Download of File:" + fileName + " from following PEERs: " + ipAddressList + " with Hash:" + sha1FileName +" having Chunks: " + nChunks  );

			int chunk = 0;
			Random randGen = new Random();
			Vector<String> chunkHolderList;
			Socket Peer;
			int prgAmount;
			int bytesRead =0;

			for (chunk = 1 ; chunk <= nChunks; chunk++) {
				// Download K th chunk
				// By picking a peer holding
				// That chunk at random.

				int rand = randGen.nextInt();
				if (rand < 0) {
					rand = rand * -1;
				}

				chunkHolderList = chunkHolders(chunk);

				rand = rand % chunkHolderList.size();
				try {
					Peer = new Socket(chunkHolderList.elementAt(rand), 8000);
					DataInputStream fromPeer = new DataInputStream(Peer.getInputStream());
					DataOutputStream toPeer = new DataOutputStream(Peer.getOutputStream());

					String rqMessage = "11 " +  userName + " "  + sha1FileName + " N"  + Integer.toString(fileChunkSizeInBytes)  +   "N N" + Integer.toString(chunk) + "N N" + grpID + "N ";
					toPeer.write(rqMessage.getBytes());

					byte[] fileChunk = new byte[fileChunkSizeInBytes];
					bytesRead = fromPeer.read(fileChunk);


					FileTools.joinChunkToFile(fileName, fileChunkSizeInBytes, chunk, fileChunk, bytesRead);


					galaxiaClientUI.addChunkAvailable(grpID, sha1FileName, chunk);
					if ( chunk == 0 )
						galaxiaClientUI.mainTracker.addFileToGroup(grpID, sha1FileName); // Announce tracker as a potenital peer for the file.
					prgAmount =  (int ) ( ( chunk+ 1 ) / nChunks );
					//galaxiaClientUI.updateProgress(prgAmount);
				} catch (Exception ex) {
					System.out.println("Error Occured " + ex.getMessage());
				}
			}       

			galaxiaClientUI.setFileFullyAvailable(grpID, sha1FileName,fileName);
	}

	public void getIPAddressList() throws IOException {
		ipAddressList = new Vector<String>();
		String userIP;
		if ( userList == null )
			return;
		
		for ( String user : userList ) {
			userIP = galaxiaClientUI.mainTracker.getUserIP(user);

			if ( userIP != null && isPeer(userIP)) {
				ipAddressList.add(userIP);
				if ( isSeed( userIP  ) ) {
					seederLoc = ipAddressList.size() - 1;
				}
			}
		}
	}

	protected int findChunks() {
		int nChunks = 0;
		try {
			Socket Seeder = new Socket(ipAddressList.elementAt(seederLoc), 8000);
			String rqMessage = "12  " + userName + " " + sha1FileName + " N" + fileChunkSizeInBytes +"N N"+ grpID + "N ";

			DataInputStream fromSeeder = new DataInputStream(Seeder.getInputStream());
			DataOutputStream toSeeder = new DataOutputStream(Seeder.getOutputStream());

			toSeeder.write(rqMessage.getBytes());

			byte[] rplMessage = new byte[128];
			String rplMsg;
			fromSeeder.read(rplMessage);
			rplMsg = new String(rplMessage);
			nChunks = Integer.parseInt(rplMsg.trim());

		} catch (Exception ex) {
			System.out.println("Error Occured" + ex.getMessage());
		}

		return nChunks;
	}

	protected Vector<String> chunkHolders(long chunkNumber) {
		Vector<String> chunkHolders = new Vector<String>();

		for (String ipAddress : ipAddressList) {
			if (isChunkHolder(chunkNumber, ipAddress)) {
				chunkHolders.add(ipAddress);
			}
		}

		return chunkHolders;
	}

	protected boolean isChunkHolder(long chunkNumber, String ipAddress) {
		try {
			Socket Peer = new Socket(ipAddress, 8000);
			String rqMessage = "10  " + sha1FileName + " " + grpID + " ";

			DataInputStream fromSeeder = new DataInputStream(Peer.getInputStream());
			DataOutputStream toSeeder = new DataOutputStream(Peer.getOutputStream());

			toSeeder.write(rqMessage.getBytes());

			byte[] rplMsg = new byte[1024];
			fromSeeder.read(rplMsg);
			String replyMessage = new String(rplMsg);

			StringTokenizer st = new StringTokenizer(replyMessage, " ");

			st.nextToken();
			if (Integer.parseInt(st.nextToken()) == 1) {
				return true;
			} else {
				while (st.hasMoreTokens()) {
					if (Integer.parseInt(st.nextToken()) == chunkNumber) {
						return true;
					}
				}
			}


		} catch (Exception ex) {
			System.out.println("Error Occured" + ex.getMessage());
		}

		return false;
	}
	protected boolean isPeer( String ipAddress) {
		try {
			Socket Peer = new Socket(ipAddress, 8000);
			String rqMessage = "10  " + sha1FileName + " " + grpID + " ";

			DataInputStream fromSeeder = new DataInputStream(Peer.getInputStream());
			DataOutputStream toSeeder = new DataOutputStream(Peer.getOutputStream());

			toSeeder.write(rqMessage.getBytes());

			byte[] rplMsg = new byte[1024];
			fromSeeder.read(rplMsg);
			String replyMessage = new String(rplMsg);

			StringTokenizer st = new StringTokenizer(replyMessage, " ");

			st.nextToken();
			if (Integer.parseInt(st.nextToken()) == 1) {
				return true;
			} else {
				if (st.hasMoreTokens())
					return true;
			}


		} catch (Exception ex) {
			System.out.println("Error Occured" + ex.getMessage());
		}

		return false;
	}
	protected boolean isSeed( String ipAddress) {
		try {
			Socket Peer = new Socket(ipAddress, 8000);
			String rqMessage = "10  " + sha1FileName + " " + grpID + " ";

			DataInputStream fromSeeder = new DataInputStream(Peer.getInputStream());
			DataOutputStream toSeeder = new DataOutputStream(Peer.getOutputStream());

			toSeeder.write(rqMessage.getBytes());

			byte[] rplMsg = new byte[1024];
			fromSeeder.read(rplMsg);
			String replyMessage = new String(rplMsg);

			StringTokenizer st = new StringTokenizer(replyMessage, " ");

			st.nextToken();
			if (Integer.parseInt(st.nextToken()) == 1) {
				return true;
			} else {
				return false;
			}


		} catch (Exception ex) {
			System.out.println("Error Occured" + ex.getMessage());
		}

		return false;
	}

	protected Thread downloaderThread;
	protected TrackerCom tc;
	protected Vector<String> ipAddressList;
	protected Vector<String> userList;
	protected String userName;
	protected String sha1FileName;
	protected String fileName;
	protected GalaxiaClientUI galaxiaClientUI;
	protected int fileChunkSizeInBytes;
	protected int grpID;
	protected int seederLoc;
	protected boolean quit;
}
